]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
shell-util: Add async-signal-safe wrappers for GLib.spawn_async implementations
authorMarco Trevisan (Treviño) <mail@3v1n0.net>
Tue, 30 Apr 2024 22:27:19 +0000 (00:27 +0200)
committerFlorian Müllner <fmuellner@gnome.org>
Fri, 24 May 2024 23:12:51 +0000 (01:12 +0200)
In the shell code we often use GLib.spawn_async to launch processes
with a GSpawnChildSetupFunc implementation in JavaScript to reset the
mutter nofile rlimit in the new child process.

However, this is highly unsafe to do because this implies that the child
setup function code is executed in gjs where a lot of allocations are
done and even more not-async-signal-safe code is executed, in fact
leading to dead-locks as reported in the past.

To prevent this, declare a new functions that do the same of the GLib
counterpart but without providing a GSpawnChildSetupFunc that is instead
implemented in the C-side doing the cleanup that mutter requires without
allocations or async-signal-unsafe code.

Helps: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/6698
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3293>
(cherry picked from commit 781010be66c0f4c0c13c99860a333d37a17259ea)

src/shell-util.c
src/shell-util.h

index 00e3aa16c21c10ef6c2469dabe7af8baf30af4d5..311576c0e9a968696207706edacef0eb1dce5f61 100644 (file)
 #include <cogl/cogl.h>
 
 #include "shell-app-cache-private.h"
+#include "shell-global.h"
 #include "shell-util.h"
 #include <glib/gi18n-lib.h>
 #include <gdk-pixbuf/gdk-pixbuf.h>
+#include <meta/meta-context.h>
 #include <meta/display.h>
 #include <meta/meta-x11-display.h>
 
@@ -810,3 +812,175 @@ shell_util_get_translated_folder_name (const char *name)
 {
   return shell_app_cache_translate_folder (shell_app_cache_get_default (), name);
 }
+
+static void
+spawn_child_setup (gpointer user_data)
+{
+  MetaContext *meta_context = user_data;
+
+  /* No async-signal-unsafe code should be called here, so we don't either
+   * pass the GError to the function, as it may lead to dynamic allocations.
+   */
+  meta_context_restore_rlimit_nofile (meta_context, NULL);
+}
+
+/**
+ * shell_util_spawn_async_with_pipes_and_fds:
+ * @working_directory: (type filename) (nullable): child's current working
+ *     directory, or %NULL to inherit parent's, in the GLib file name encoding
+ * @argv: (array zero-terminated=1): child's argument
+ *     vector, in the GLib file name encoding; it must be non-empty and %NULL-terminated
+ * @envp: (array zero-terminated=1) (nullable):
+ *     child's environment, or %NULL to inherit parent's, in the GLib file
+ *     name encoding
+ * @flags: flags from #GSpawnFlags
+ * @stdin_fd: file descriptor to use for child's stdin, or `-1`
+ * @stdout_fd: file descriptor to use for child's stdout, or `-1`
+ * @stderr_fd: file descriptor to use for child's stderr, or `-1`
+ * @source_fds: (array length=n_fds) (nullable): array of FDs from the parent
+ *    process to make available in the child process
+ * @target_fds: (array length=n_fds) (nullable): array of FDs to remap
+ *    @source_fds to in the child process
+ * @n_fds: number of FDs in @source_fds and @target_fds
+ * @stdin_pipe_out: (out) (optional): return location for file descriptor to write to child's stdin, or %NULL
+ * @stdout_pipe_out: (out) (optional): return location for file descriptor to read child's stdout, or %NULL
+ * @stderr_pipe_out: (out) (optional): return location for file descriptor to read child's stderr, or %NULL
+ * @error: return location for error
+ *
+ * A wrapper around g_spawn_async_with_pipes_and_fds() with async-signal-safe
+ * implementation of #GSpawnChildSetupFunc to launch a child program
+ * asynchronously resetting the rlimit nofile on child setup.
+ *
+ * Returns: the PID of the child on success, 0 if error is set
+ **/
+GPid
+shell_util_spawn_async_with_pipes_and_fds (const char          *working_directory,
+                                           const char * const  *argv,
+                                           const char * const  *envp,
+                                           GSpawnFlags          flags,
+                                           int                  stdin_fd,
+                                           int                  stdout_fd,
+                                           int                  stderr_fd,
+                                           const int           *source_fds,
+                                           const int           *target_fds,
+                                           size_t               n_fds,
+                                           int                 *stdin_pipe_out,
+                                           int                 *stdout_pipe_out,
+                                           int                 *stderr_pipe_out,
+                                           GError             **error)
+{
+  ShellGlobal *shell_global = shell_global_get ();
+  GPid child_pid = 0;
+
+  if (!g_spawn_async_with_pipes_and_fds (working_directory, argv, envp, flags,
+                                         spawn_child_setup, shell_global,
+                                         stdin_fd, stdout_fd, stderr_fd,
+                                         source_fds, target_fds, n_fds,
+                                         &child_pid,
+                                         stdin_pipe_out, stdout_pipe_out, stderr_pipe_out,
+                                         error))
+    return 0;
+
+  return child_pid;
+}
+
+/**
+ * shell_util_spawn_async_with_pipes:
+ * @working_directory: (type filename) (nullable): child's current working
+ *     directory, or %NULL to inherit parent's
+ * @argv: (array zero-terminated=1):
+ *     child's argument vector
+ * @envp: (array zero-terminated=1) (nullable):
+ *     child's environment, or %NULL to inherit parent's
+ * @flags: flags from #GSpawnFlags
+ * @standard_input: (out) (optional): return location for file descriptor to write to child's stdin, or %NULL
+ * @standard_output: (out) (optional): return location for file descriptor to read child's stdout, or %NULL
+ * @standard_error: (out) (optional): return location for file descriptor to read child's stderr, or %NULL
+ * @error: return location for error
+ *
+ * A wrapper around g_spawn_async_with_pipes() with async-signal-safe
+ * implementation of #GSpawnChildSetupFunc to launch a child program
+ * asynchronously resetting the rlimit nofile on child setup.
+ *
+ * Returns: the PID of the child on success, 0 if error is set
+ **/
+GPid
+shell_util_spawn_async_with_pipes (const char          *working_directory,
+                                   const char * const  *argv,
+                                   const char * const  *envp,
+                                   GSpawnFlags          flags,
+                                   int                 *standard_input,
+                                   int                 *standard_output,
+                                   int                 *standard_error,
+                                   GError             **error)
+{
+  return shell_util_spawn_async_with_pipes_and_fds (working_directory, argv, envp, flags,
+                                                    -1, -1, -1, NULL, NULL, 0,
+                                                    standard_input, standard_output, standard_error,
+                                                    error);
+}
+
+/**
+ * shell_util_spawn_async_with_fds:
+ * @working_directory: (type filename) (nullable): child's current working
+ *     directory, or %NULL to inherit parent's
+ * @argv: (array zero-terminated=1):
+ *     child's argument vector
+ * @envp: (array zero-terminated=1) (nullable):
+ *     child's environment, or %NULL to inherit parent's
+ * @flags: flags from #GSpawnFlags
+ * @stdin_fd: file descriptor to use for child's stdin, or `-1`
+ * @stdout_fd: file descriptor to use for child's stdout, or `-1`
+ * @stderr_fd: file descriptor to use for child's stderr, or `-1`
+ * @error: return location for error
+ *
+ * A wrapper around g_spawn_async_with_fds() with async-signal-safe
+ * implementation of #GSpawnChildSetupFunc to launch a child program
+ * asynchronously resetting the rlimit nofile on child setup.
+ *
+ * Returns: the PID of the child on success, 0 if error is set
+ **/
+GPid
+shell_util_spawn_async_with_fds (const char          *working_directory,
+                                 const char * const  *argv,
+                                 const char * const  *envp,
+                                 GSpawnFlags          flags,
+                                 int                  stdin_fd,
+                                 int                  stdout_fd,
+                                 int                  stderr_fd,
+                                 GError             **error)
+{
+  return shell_util_spawn_async_with_pipes_and_fds (working_directory, argv, envp, flags,
+                                                    stdin_fd, stdout_fd, stderr_fd,
+                                                    NULL, NULL, 0,
+                                                    NULL, NULL, NULL,
+                                                    error);
+}
+
+/**
+ * shell_util_spawn_async:
+ * @working_directory: (type filename) (nullable): child's current working
+ *     directory, or %NULL to inherit parent's
+ * @argv: (array zero-terminated=1):
+ *     child's argument vector
+ * @envp: (array zero-terminated=1) (nullable):
+ *     child's environment, or %NULL to inherit parent's
+ * @flags: flags from #GSpawnFlags
+ * @error: return location for error
+ *
+ * A wrapper around g_spawn_async() with async-signal-safe implementation of
+ * #GSpawnChildSetupFunc to launch a child program asynchronously resetting the
+ * rlimit nofile on child setup.
+ *
+ * Returns: the PID of the child on success, 0 if error is set
+ **/
+GPid
+shell_util_spawn_async (const char          *working_directory,
+                        const char * const  *argv,
+                        const char * const  *envp,
+                        GSpawnFlags          flags,
+                        GError             **error)
+{
+  return shell_util_spawn_async_with_pipes (working_directory, argv, envp,
+                                            flags, NULL, NULL, NULL, error);
+}
index 03e75abeceef8c640b4f21bea4ccb0622fabd6ae..8464fbc2b060550d84d7c0f07a930202430d5113 100644 (file)
@@ -80,6 +80,45 @@ char *shell_util_get_translated_folder_name (const char *name);
 
 gint shell_util_get_uid (void);
 
+GPid shell_util_spawn_async_with_pipes_and_fds (const char          *working_directory,
+                                                const char * const  *argv,
+                                                const char * const  *envp,
+                                                GSpawnFlags          flags,
+                                                int                  stdin_fd,
+                                                int                  stdout_fd,
+                                                int                  stderr_fd,
+                                                const int           *source_fds,
+                                                const int           *target_fds,
+                                                size_t               n_fds,
+                                                int                 *stdin_pipe_out,
+                                                int                 *stdout_pipe_out,
+                                                int                 *stderr_pipe_out,
+                                                GError             **error);
+
+GPid shell_util_spawn_async_with_pipes (const char          *working_directory,
+                                        const char * const  *argv,
+                                        const char * const  *envp,
+                                        GSpawnFlags          flags,
+                                        int                 *standard_input,
+                                        int                 *standard_output,
+                                        int                 *standard_error,
+                                        GError             **error);
+
+GPid shell_util_spawn_async_with_fds (const char          *working_directory,
+                                      const char * const  *argv,
+                                      const char * const  *envp,
+                                      GSpawnFlags          flags,
+                                      int                  stdin_fd,
+                                      int                  stdout_fd,
+                                      int                  stderr_fd,
+                                      GError             **error);
+
+GPid shell_util_spawn_async (const char          *working_directory,
+                             const char * const  *argv,
+                             const char * const  *envp,
+                             GSpawnFlags          flags,
+                             GError             **error);
+
 G_END_DECLS
 
 #endif /* __SHELL_UTIL_H__ */